//
// EpollServer.cpp
//
// $Id: //poco/1.4/Net/src/EpollServer.cpp#2 $
//
// Library: Net
// Package: Sockets
// Module:  EpollServer
//
// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#include "Poco/Net/EpollServer.h"
#include "Poco/Exception.h"

#include <sys/time.h>
#include <sys/resource.h>

//#include <iostream>

using Poco::InvalidArgumentException;


namespace Poco {
namespace Net {


EpollServer::EpollServer(const SocketAddress& address,EpollDispatcher& dispather,bool reuseAddress,EPOLL_MODE mode, int backlog)
:m_dispatcher(dispather)
,m_mode(mode)
{
	Socket::bind(address, reuseAddress);
	Socket::listen(backlog);
	m_epollFd = 0;
	m_exitflag =false;
	init();
}


EpollServer::EpollServer(Poco::UInt16 port,EpollDispatcher& dispather,bool reuseAddress,EPOLL_MODE mode, int backlog)
:m_dispatcher(dispather)
,m_mode(mode)
{
	IPAddress wildcardAddr;	/// 0.0.0.0
	SocketAddress address(wildcardAddr, port);
	Socket::bind(address, reuseAddress);
	Socket::listen(backlog);
	m_epollFd = 0;
	m_exitflag =false;
	init();
}


EpollServer& EpollServer::operator = (const Socket& socket)
{
	Socket::operator = (socket);
	return *this;
}


EpollServer::~EpollServer()
{
	/// 资源类的都需要手动关闭
	stopWait();
	poco_closesocket(m_epollFd);
	
	if (m_timeoutTarget)
		m_timeoutTarget->stop();
	
	m_timeoutThread.join();
	if (m_timeoutTarget)
	{
		delete m_timeoutTarget;
		m_timeoutTarget = NULL;
	}
}


void EpollServer::init(Poco::UInt32 maxopenfd)
{
    struct rlimit rt;
    /* 设置每个进程允许打开的最大文件数 */
    rt.rlim_max = rt.rlim_cur = maxopenfd;
    setrlimit(RLIMIT_NOFILE, &rt);
	
	// epoll init
	 /*
     * Since Linux 2.6.8, the size argument is ignored, but must be greater than zero
     */
    m_epollFd   = epoll_create(maxopenfd);
    struct epoll_event ev;
    ev.data.fd  = sockfd();
	if (m_mode == EPOLLMODE_ET)
	{
		ev.events   = EPOLLIN | EPOLLET;
		Socket::setBlocking(sockfd(),false);
	}
	else
	{
		ev.events   = EPOLLIN;
	}
	
    epoll_ctl(m_epollFd,EPOLL_CTL_ADD,sockfd(),&ev);
	m_event = 0;
	m_timeoutTarget = new FdTimeOutThread(*this,m_epollFd,m_dispatcher);
	m_timeoutThread.start(*m_timeoutTarget);
}

// void EpollServer::setTimeoutFds(const std::set<int>& timeoutFds)
// {
// 	Poco::ScopedLock<Poco::Mutex>	lock(m_timeoutFdsMutex);
// 	if (m_timeoutFds.empty())
// 	{
// 		m_timeoutFds = timeoutFds;
// 	}
// 	else
// 	{
// 		m_timeoutFds.insert(timeoutFds.begin(),timeoutFds.end());
// 	}
// }

void EpollServer::addTimeoutFd(int timeoutFd)
{
	Poco::ScopedLock<Poco::Mutex>	lock(m_timeoutFdsMutex);
	m_timeoutFds.insert(timeoutFd);
}

void EpollServer::getTimeoutFds(std::set<int>& timeoutFds)
{
	Poco::ScopedLock<Poco::Mutex>	lock(m_timeoutFdsMutex);
	timeoutFds = m_timeoutFds;
	m_timeoutFds.clear();
}

void EpollServer::wait()
{
	int nfds 					= 0;
    int etimeout 				= 10;         //mili second
    
    struct epoll_event 			ev;
    ev.data.fd  				= 0;
	if (m_mode == EPOLLMODE_ET)
	{
		ev.events   				= EPOLLIN|EPOLLRDHUP|EPOLLET;
	}
	else
	{
		ev.events   				= EPOLLIN|EPOLLRDHUP;
	}
    m_event						= ev.events;
    std::vector<epoll_event> 	events;
    events.resize(MAXOPENFD);
    m_maxevent   			= 1;		// include m_epollFd
    while(!m_exitflag)
    {
        nfds = epoll_wait(m_epollFd,&events[0],events.size(),etimeout);
        for(int i = 0; i < nfds; ++i)
        {
            if(events[i].data.fd == sockfd())
            {
				int tmpfd = 0;
				while( (tmpfd = acceptConnection())  > 0)
				{
					ev.data.fd   = tmpfd;
					ev.events    |= EPOLLERR;
					if (m_mode == EPOLLMODE_ET)
					{
						Socket::setBlocking(tmpfd,false);
						Socket::setNoDelay(tmpfd,true);
					}
					if( 0 == epoll_ctl(m_epollFd,EPOLL_CTL_ADD,tmpfd,&ev))
					{
						++m_maxevent;
					}
				}
            }
            // 客户端 close 会同时触发 EPOLLIN 和 EPOLLRDHUP，用EPOLLIN只表示可读
            else if (events[i].events&EPOLLRDHUP || events[i].events&EPOLLERR || events[i].events&EPOLLHUP )
            {
				addTimeoutFd(events[i].data.fd);
            }
            else if(events[i].events&EPOLLIN)
            {
				m_dispatcher.enqueue(events[i].data.fd);
            }
        }
        events.clear();
        events.resize(m_maxevent);
    }
}


int EpollServer::acceptConnection()
{
	SocketAddress clientAddr;
	return Socket::acceptConnection(clientAddr);
}


} } // namespace Poco::Net











